package org.duomn.ichat.thread;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketException;

import org.duomn.ichat.entity.Message;
import org.duomn.ichat.entity.MsgCatalog;
import org.duomn.ichat.exception.MsgLoseException;
import org.duomn.ichat.net.MsgHandler;
import org.duomn.ichat.net.MsgSender;
import org.duomn.ichat.net.UIChatMsgHandler;
import org.duomn.ichat.net.impl.ChatMsgHander;
import org.duomn.ichat.net.impl.ChatMsgSender;
import org.duomn.ichat.net.impl.FileMsgHandler;
import org.duomn.ichat.net.impl.FileMsgSender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class TCPThreadAbstract extends Thread implements MsgHandler {
	
	private static Logger logger = LoggerFactory.getLogger(TCPThreadAbstract.class);
	
	protected Socket socket;
	protected ObjectInputStream in;
	protected ObjectOutputStream out;
	
	protected boolean isrunning;
	
	protected MsgSender msgSender;
	
	protected MsgHandler msgHandler;
	
	protected UIChatMsgHandler uiHandler;
	
	// 发起关闭
	protected boolean isSponsor; 
	
	public void setMsgSender(MsgSender sender) {
		this.msgSender = sender;
	}
	
	public void setUIHandler(UIChatMsgHandler uiHandler) {
		this.uiHandler = uiHandler;
	}
	
	public UIChatMsgHandler getUIHandler() {
		return this.uiHandler;
	}
	
	public boolean isRunning() {
		return !isrunning;
	}
	
	public abstract boolean openSession();
	
	protected boolean initStream() {
		try {
			out = new ObjectOutputStream(socket.getOutputStream());
			in = new ObjectInputStream(socket.getInputStream());
			if (msgSender != null) {
				msgSender.injectSendStream(out);
			}
		} catch (SocketException e) { // 测试端口的线程会在open成功前，关闭socket
			logger.debug("只是一个测试线程");
			return false;
		} catch (IOException e) {
			logger.error("创建被动聊天线程异常...", e);
			return false;
		}
		isrunning = true;
		this.start();
		logger.debug("被动线程启动，来自Socket:" + socket.getPort());
		return true;
	}

	/** 读取消息的主循环 */
	@Override
	public void run() { // 被动线程获取数据的方法
		while (isrunning) { 
			logger.debug("线程启动完成，本地端口:" + socket.getLocalPort() + "来自远程端口:" + socket.getPort());
			try {
				handle();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SocketException e) {
				logger.warn("socket异常，线程强制退出");
				isrunning = false;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		logger.debug("线程退出完成");
	}
	
	@Override
	public void handle() throws ClassNotFoundException,
			SocketException, IOException {
		// TODO 在此处根据第一次收到的消息，为被动线程创建对应的消息处理器
		if (msgHandler == null) { // 第一次为空，判断需要创建哪一种处理器
			Message message = (Message) in.readObject();
			if (message.msgCatalog == MsgCatalog.CHAT_MSG) {
				if (msgSender == null) { // 创建被动线程的发送器
					msgSender = new ChatMsgSender(); 
					msgSender.injectSendStream(out);
				}
				msgHandler = new ChatMsgHander(this, message); // 创建被动线程的处理器
			} else if (message.msgCatalog == MsgCatalog.FILE_TRAN) {
				if (msgSender == null) { // 创建被动线程的发送器
					msgSender = new FileMsgSender();
					msgSender.injectSendStream(out);
				}
				msgHandler = new FileMsgHandler(this, message);
			}
		} else {
			msgHandler.handle();
		}
	}

	public void send(Message message) throws MsgLoseException {
		msgSender.send(message);
	}
	
	// 关闭Socket（主动线程和被动线程之间的Socket）
	public void closeSession() { 
		logger.debug("发起关闭会话请求，发送关闭Sokect信号");
		isSponsor = true;
		try {
			msgSender.sendClose();
		} catch (MsgLoseException e) {
			logger.error("发送关闭会话请求发生异常，强制退出线程");
			isrunning = false;
			closeSocket();
		}
	}
	
	public void closeByReceive() {
		if (isSponsor()) {
			logger.debug("主动方在被动方退出后，进行退出");
			stopLoop(); // 停止循环
			closeSocket(); // 关闭socket
		} else {
			logger.debug("线程收到退出信号，进行退出，并通知发送方自己成功退出");
			try {
				msgSender.sendClose();
			} catch (MsgLoseException e) {
				logger.warn("转发退出信号异常，强行退出");
				e.printStackTrace();
			} // 被动方接受到关闭信号后，停止自己的循环（读数据），同时向主动放发送关闭
			stopLoop();
		}
	}
	
	private boolean isSponsor() {
		return isSponsor;
	}
	
	private void stopLoop() {
		isrunning = false;
	}
	
	private void closeSocket() {
		if (socket == null) return;
		try {
			socket.close();
			socket = null;
		} catch (IOException e) {
			logger.error("关闭socket异常", e);
		}
	}
	
	public ObjectInputStream getInputStream() {
		return in;
	}

	public ObjectOutputStream getOutStream() {
		return out;
	}
}
